package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"text/template"
)

func getTemplate(path, name string) (*template.Template, error) {
	letter, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	temp, err := template.New(name).Funcs(funcsMap).Parse(string(letter))
	if err != nil {
		panic(err)
	}
	return temp.Parse(goTypeGenLetter)
}

func GenDataFile(suffix string, opt *Options, tmpl *template.Template) error {
	suffix = strings.ToLower(suffix)
	// 每个结构体一个文件 用 struct name.suffix 拼接 都在out 目录下
	for idx, st := range opt.jsonData.Structs {
		if st == nil {
			Logf("序号%d 结构体数据为nil", idx)
			continue
		}
		if st.Name == "" {
			continue
		}

		//生成data 引用文件
		gen := &DataInfo{
			Struct:  st,
			IdlName: opt.jsonData.IdlName,
		}

		// 计算结构体内部的依赖
		deps := make(map[string]bool)
		for _, v := range gen.Struct.Fields {
			if v.IsStruct || v.IsEnum {
				deps[v.Type] = true
			}

			if v.Key != nil {
				if v.Key.IsStruct {
					deps[v.Key.Type] = true
				}
			}

			if v.Value != nil {
				if v.Value.IsStruct {
					deps[v.Value.Type] = true
				}
			}
		}

		// 遍历添加依赖
		for k, _ := range deps {
			gen.Deps = append(gen.Deps, k)
		}

		stName := strings.ToLower(st.Name)
		toFile := fmt.Sprintf("%s.%s", stName, suffix)
		err := doTemplate(toFile, tmpl, gen)
		if err != nil {
			Logf("根据模板生成文件%s失败, 错误为 %v", suffix, err)
		} else {
			Logf("根据模板%s生成文件%s成功", suffix, toFile)
		}
	}
	return nil
}

func GenTableFile(suffix string, opt *Options, tmpl *template.Template) error {
	suffix = strings.ToLower(suffix)
	// 每个结构体一个文件 用 struct name.suffix 拼接 都在out 目录下
	for idx, tb := range opt.jsonData.Tables {
		if tb == nil {
			Logf("序号%d 结构体数据为nil", idx)
			continue
		}
		if tb.Name == "" {
			continue
		}

		//生成data 引用文件
		gen := &TableInfo{
			Table:   tb,
			IdlName: opt.jsonData.IdlName,
		}

		stName := strings.ToLower(tb.Name)
		toFile := fmt.Sprintf("%s.%s", stName, suffix)
		err := doTemplate(toFile, tmpl, gen)
		if err != nil {
			Logf("根据模板生成文件%s失败, 错误为 %v", suffix, err)
		} else {
			Logf("根据模板%s生成文件%s成功", suffix, toFile)
		}
	}
	return nil
}

func GenEnumFile(suffix string, opt *Options, tmpl *template.Template) error {
	suffix = strings.ToLower(suffix)
	// 每个结构体一个文件 用 struct name.suffix 拼接 都在out 目录下
	for idx, eu := range opt.jsonData.Enums {
		if eu == nil {
			Logf("序号%d 枚举数据为nil", idx)
			continue
		}
		if eu.Name == "" {
			continue
		}

		//生成data 引用文件
		gen := &DataInfo{
			Enum:    eu,
			IdlName: opt.jsonData.IdlName,
		}

		euName := strings.ToLower(eu.Name)
		toFile := fmt.Sprintf("%s.%s", euName, suffix)
		err := doTemplate(toFile, tmpl, gen)
		if err != nil {
			Logf("根据模板生成文件%s失败, 错误为 %v", suffix, err)
		} else {
			Logf("根据模板生成文件%s成功", suffix)
		}
	}
	return nil
}

func GenSdkFile(suffix string, opt *Options, tmpl *template.Template) error {
	suffix = strings.ToLower(suffix)
	for idx, sn := range opt.jsonData.ServiceNames {

		if len(opt.limit) != 0 && opt.limit != sn {
			fmt.Println("[idl2lang] skip service ", sn)
			continue
		}
		//生成data 引用文件
		gen := &SdkInfo{
			IdlName:     opt.jsonData.IdlName,
			ServiceName: sn,
			Proxy:       opt.proxy,
			Uuid:        opt.jsonData.Services[idx].Uuid,
		}

		perName := strings.ToLower(sn)
		toFile := fmt.Sprintf("%s.%s", perName, suffix)
		err := doTemplate(toFile, tmpl, gen)
		if err != nil {
			Logf("根据模板生成文件%s失败, 错误为 %v", suffix, err)
		} else {
			Logf("根据模板%s生成文件%s成功", suffix, toFile)
		}
	}
	return nil
}

func GenServiceFile(suffix string, opt *Options, tmpl *template.Template) error {
	suffix = strings.ToLower(suffix)

	for idx, v := range opt.jsonData.Services {
		//检查路径
		if opt.jsonData.ServiceNames[idx] == "" {
			continue
		}

		//检查限制
		if len(opt.limit) != 0 && opt.limit != opt.jsonData.ServiceNames[idx] {
			continue
		}

		srvpath := strings.ToLower(opt.jsonData.ServiceNames[idx])
		// 不在为了每个服务创建文件夹
		if opt.genSubDir {
			if CheckSrvSubDir(srvpath) == false {
				Logf("generate Service %s working path error！！！", opt.jsonData.ServiceNames[idx])
				continue
			}
		}

		moduleName = "model"

		//生成data 引用文件
		gen := &ServiceInfo{
			Name:        opt.jsonData.ServiceNames[idx],
			Service:     v,
			IdlName:     opt.jsonData.IdlName,
			StructNames: opt.jsonData.StructNames,
			Structs:     opt.jsonData.Structs,
			EnumNames:   opt.jsonData.EnumNames,
			Runtime:     opt.runtime,
			Backend:     opt.backend,
			Deps:        make(map[string]bool),
		}

		// 计算这个service 依赖多少的结构体
		for _, method := range v.Methods {
			for _, arg := range method.Arguments {
				if arg.IsStruct {
					gen.Deps[arg.Type] = true
				}

				if arg.Key != nil {
					if arg.Key.IsStruct {
						gen.Deps[arg.Key.Type] = true
					}
				}

				if arg.Value != nil {
					if arg.Value.IsStruct {
						gen.Deps[arg.Value.Type] = true
					}
				}
			}

			if method.RetType == nil {
				continue
			}

			if method.RetType.IsStruct {
				gen.Deps[method.RetType.Type] = true
			}

			if method.RetType.Key != nil {
				if method.RetType.Key.IsStruct {
					gen.Deps[method.RetType.Key.Type] = true
				}
			}

			if method.RetType.Value != nil {
				if method.RetType.Value.IsStruct {
					gen.Deps[method.RetType.Value.Type] = true
				}
			}
		}

		var toFile string
		if opt.genSubDir {
			if suffix == "mod" {
				toFile = fmt.Sprintf("%s/go.mod", srvpath)
			} else {
				toFile = fmt.Sprintf("%s/%s.%s", srvpath, srvpath, suffix)
			}
		} else {
			if suffix == "mod" {
				toFile = "go.mod"
			} else {
				toFile = fmt.Sprintf("%s.%s", srvpath, suffix)
			}
		}

		err := doTemplate(toFile, tmpl, gen)
		if err != nil {
			Logf("根据模板生成文件%s失败, 错误为 %v", suffix, err)
		} else {
			Logf("根据模板%s生成文件%s成功", suffix, toFile)
		}
	}

	return nil
}

func doTemplate(toFile string, tmpl *template.Template, gen interface{}) error {
	//检测文件是否存在, 前面确保过路径已经存在了
	if FileExits(toFile) {
		//存在的话删了重新生成
		err := os.Remove(toFile)
		if err != nil {
			Logf("delete %s impl file error %v !!!", toFile, err)
			return err
		}
		Logf("删除旧文件%s 重新生成！", toFile)
	}
	//打开文件
	filehanle, err := os.OpenFile(toFile, os.O_WRONLY|os.O_CREATE, 0765)
	if err != nil {
		return err
	}

	defer filehanle.Close()

	return tmpl.Execute(filehanle, gen)
}

func GenPubgo(suffix string, opt *Options, tmpl *template.Template) error {

	boxInfo := BoxsInfo{}
	jsonfile, err := os.Open(opt.input)
	if err != nil {
		Logf("read file %s error %v", opt.input, err)
		return err
	}
	defer jsonfile.Close()

	jBytes, _ := ioutil.ReadAll(jsonfile)
	err = json.Unmarshal(jBytes, &boxInfo)
	if err != nil {
		return err
	}
	return doTemplate(opts.toFile, tmpl, &boxInfo)
}
